package com.darwin.darwinGpuEngine.core.materials {

    import com.darwin.darwinGpuEngine.core.geom.Face;
    import com.darwin.darwinGpuEngine.core.geom.UV;
    import com.darwin.darwinGpuEngine.core.geom.Vertex;
    import com.darwin.darwinGpuEngine.utils.NodeBlendMode;
    
    import com.darwin.darwinGpuEngine.core.materials.BlendModePresets;
    
    import flash.display3D.Context3D;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.VertexBuffer3D;
    import flash.geom.Matrix3D;
    import flash.geom.Rectangle;
    import flash.utils.Dictionary;

    public class AMaterial {

        //摄像机视角变换矩阵
        public var viewProjectionMatrix:Matrix3D;

        // 摄像机视角变换矩阵
        public var projectionMatrix:Matrix3D;

        // 模型矩阵
        public var modelMatrix:Matrix3D;

        public var clipSpaceMatrix:Matrix3D = new Matrix3D();

        public var numTris:int = 0;
        public var drawCalls:int = 0;

        public var blendMode:NodeBlendMode = BlendModePresets.NORMAL_PREMULTIPLIED_ALPHA;

        public var needUploadVertexBuffer:Boolean = false;

        protected var indexBuffer:IndexBuffer3D;
        protected var vertexBuffer:VertexBuffer3D;

        protected var mIndexBuffer:Vector.<uint>;
        protected var mVertexBuffer:Vector.<Number>;

        protected var programData:ProgramData;

        public static const VERTEX_POSITION:String = "PB3D_POSITION";
        public static const VERTEX_UV:String = "PB3D_UV";

        public function AMaterial() {

        }

        protected function generateBufferData(context:Context3D, faceList:Vector.<Face>):void {

            if(vertexBuffer) {
                return;
            }

            initProgram(context);

            var i:int;
            var numFaces:int = faceList.length;
            var numIndices:int;

            mIndexBuffer = new Vector.<uint>();
            mVertexBuffer = new Vector.<Number>();

            var duplicateCheck:Dictionary = new Dictionary();
            var tmpUID:String;
            var indexBufferIdx:uint = 0;
            var face:Face;

            // 生成索引和顶点缓冲
            // 检查顶点是否已经添加到缓冲区，如果是则跳过
            for(i = 0; i < numFaces; i++) {

                face = faceList[i];

                tmpUID = face.v1.uid + "." + face.uv1.uid;

                if(duplicateCheck[tmpUID] == undefined) {
                    addVertex(context, mVertexBuffer, face.v1, face.uv1, face);
                    duplicateCheck[tmpUID] = indexBufferIdx;
                    mIndexBuffer.push(indexBufferIdx);
                    face.v1.bufferIdx = indexBufferIdx;
                    ++indexBufferIdx;
                } else {
                    mIndexBuffer.push(duplicateCheck[tmpUID]);
                }

                tmpUID = face.v2.uid + "." + face.uv2.uid;

                if(duplicateCheck[tmpUID] == undefined) {
                    addVertex(context, mVertexBuffer, face.v2, face.uv2, face);
                    duplicateCheck[tmpUID] = indexBufferIdx;
                    mIndexBuffer.push(indexBufferIdx);
                    face.v2.bufferIdx = indexBufferIdx;
                    ++indexBufferIdx;
                } else {
                    mIndexBuffer.push(duplicateCheck[tmpUID]);
                }

                tmpUID = face.v3.uid + "." + face.uv3.uid;

                if(duplicateCheck[tmpUID] == undefined) {
                    addVertex(context, mVertexBuffer, face.v3, face.uv3, face);
                    duplicateCheck[tmpUID] = indexBufferIdx;
                    mIndexBuffer.push(indexBufferIdx);
                    face.v3.bufferIdx = indexBufferIdx;
                    ++indexBufferIdx;
                } else {
                    mIndexBuffer.push(duplicateCheck[tmpUID]);
                }
            }

            duplicateCheck = null;
            numIndices = mVertexBuffer.length / programData.numFloatsPerVertex;

            // 生成缓冲区
            vertexBuffer = context.createVertexBuffer(numIndices, programData.numFloatsPerVertex);
            vertexBuffer.uploadFromVector(mVertexBuffer, 0, numIndices);

            if(!indexBuffer) {

                indexBuffer = context.createIndexBuffer(mIndexBuffer.length);
                indexBuffer.uploadFromVector(mIndexBuffer, 0, mIndexBuffer.length);

                numTris = int(mIndexBuffer.length / 3);
            }
        }

        protected function prepareForRender(context:Context3D):void {

            context.setProgram(programData.program);
            context.setBlendFactors(blendMode.src, blendMode.dst);

            if(needUploadVertexBuffer) {
                needUploadVertexBuffer = false;
                vertexBuffer.uploadFromVector(mVertexBuffer, 0, mVertexBuffer.length / programData.numFloatsPerVertex);
            }
        }

        public function handleDeviceLoss():void {
            indexBuffer = null;
            vertexBuffer = null;
            mIndexBuffer = null;
            mVertexBuffer = null;
            programData = null;
            needUploadVertexBuffer = true;
        }

        public function render(context:Context3D, faceList:Vector.<Face>, startTri:uint, numTris:uint):void {
            generateBufferData(context, faceList);
            prepareForRender(context)
            context.drawTriangles(indexBuffer, startTri * 3, numTris);
            clearAfterRender(context);
        }

        protected function clearAfterRender(context:Context3D):void {
            throw new Error("在继承此类的素材类里实现此方法");
        }

        protected function initProgram(context:Context3D):void {
            throw new Error("在继承此类的素材类里实现此方法");
        }

        protected function refreshClipspaceMatrix():Matrix3D {
            clipSpaceMatrix.identity();
            clipSpaceMatrix.append(modelMatrix);
            clipSpaceMatrix.append(viewProjectionMatrix);
            return clipSpaceMatrix;
        }

        protected function addVertex(context:Context3D, buffer:Vector.<Number>, v:Vertex, uv:UV, face:Face):void {
            throw new Error("在继承此类的素材类里实现此方法");
        }

        protected function fillBuffer(buffer:Vector.<Number>, v:Vertex, uv:UV, face:Face, semanticsID:String, floatFormat:int):void {

            if(semanticsID == VERTEX_POSITION) {

                buffer.push(v.x, v.y);

                if(floatFormat >= 3)
                    buffer.push(v.z);

                if(floatFormat == 4)
                    buffer.push(v.w);
            }

            if(semanticsID == VERTEX_UV) {

                buffer.push(uv.u, uv.v);

                if(floatFormat == 3)
                    buffer.push(0.0);

                if(floatFormat == 4)
                    buffer.push(0.0, 0.0);
            }
        }

        protected function getFloatFormat(format:String):int {
            if(format == Context3DVertexBufferFormat.FLOAT_1)
                return 1;
            if(format == Context3DVertexBufferFormat.FLOAT_2)
                return 2;
            if(format == Context3DVertexBufferFormat.FLOAT_3)
                return 3;
            if(format == Context3DVertexBufferFormat.FLOAT_4)
                return 4;

            throw new Error("无法识别的格式");
        }

        public function cleanUp():void {
            if(indexBuffer) {
                indexBuffer.dispose();
                indexBuffer = null;
            }
            if(vertexBuffer) {
                vertexBuffer.dispose();
                vertexBuffer = null;
            }
            if(programData) {
                // 不要销毁program，它被缓存在显卡中，销毁会造成问题
                //programData.program.dispose();
                programData = null;
            }
        }
    }
}
